package com.fsy.its.netty.gbt.encoder;

import com.fsy.its.netty.gbt.decoder.GbtFrameDecoder;
import com.fsy.its.netty.msg.BaseMsg;
import com.fsy.its.util.HexUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public abstract class GbtMessageEncoder<T> extends MessageToByteEncoder<T> {

    @Override
    protected void encode(ChannelHandlerContext ctx, T msg, ByteBuf out) throws Exception {
        BaseMsg baseMsg = (BaseMsg) msg;

        //启动符
        out.writeBytes(GbtFrameDecoder.FRAME_START);

        //消息头（控制单元）
        out.writeShortLE(baseMsg.head.getSequence());
        out.writeShortLE(baseMsg.head.getVersion());
        out.writeByte(baseMsg.head.getTime().getSecond());
        out.writeByte(baseMsg.head.getTime().getMinute());
        out.writeByte(baseMsg.head.getTime().getHour());
        out.writeByte(baseMsg.head.getTime().getDayOfMonth());
        out.writeByte(baseMsg.head.getTime().getMonthValue());
        out.writeByte(baseMsg.head.getTime().getYear());
        writeAddr(out, baseMsg.head.getSrcAddr());
        writeAddr(out, baseMsg.head.getDstAddr());

        //还不知道应用单元数据由多长，记录写应用单元数据长度的位置
        int appDataLenWriterIndex = out.writerIndex();
        out.writerIndex(out.writerIndex()+2);
        //指令码
        out.writeByte(baseMsg.head.getCommand());

        //准备写应用单元数据，记录当前写点，以便知道应用单元数据有多长
        int appDataStart = out.writerIndex();
        doEncode(ctx, msg, out);

        //应用单元数据长度
        int appDataLen = out.writerIndex()-appDataStart;
        out.markWriterIndex();
        out.writerIndex(appDataLenWriterIndex);
        out.writeShortLE(appDataLen);
        out.resetWriterIndex();

        //校验和
        ByteBuf data = out.slice(GbtFrameDecoder.FRAME_START.length,
                out.readableBytes()-GbtFrameDecoder.FRAME_START.length);
        short checkSum = HexUtil.checkSum(ByteBufUtil.getBytes(data));
        out.writeByte(checkSum);

        //结束符
        out.writeBytes(GbtFrameDecoder.FRAME_END);
    }

    private void writeAddr(ByteBuf out, String addr) {
        //源和目标地址必须是6字节
        byte[] addrBuf = ByteBufUtil.decodeHexDump(addr);
        if( addrBuf.length >= 6 ) {
            out.writeBytes(addrBuf, 0, 6);
        }
        else {
            out.writeBytes(addrBuf);
            out.writeBytes(new byte[6-addrBuf.length]);
        }
    }

    protected abstract void doEncode(ChannelHandlerContext ctx, T msg, ByteBuf out);
}
